package com.wsoft.bpm.component.sign;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.wsoft.bpm.component.command.GetProcessDefinitionCacheEntryCmd;
import com.wsoft.bpm.component.command.TaskJumpCmd;
import com.wsoft.bpm.entity.ActAddSignEntity;
import com.wsoft.bpm.mapper.ActAddSignMapper;
import com.wsoft.bpm.utils.ActivityUtils;
import com.wsoft.core.utils.LoginUserUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionCacheEntry;
import org.apache.ibatis.session.SqlSession;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 动态用户任务组件
 *
 * @author zhonglj
 * @date 2024/12/3 11:16
 **/
@Slf4j
public class DynamicUserTaskComponent {

    /**
     * @param procDefId     流程定义 ID
     * @param procInstId    流程实例 ID
     * @param processEngine 流程引擎
     * @param assigns       分配人员
     * @param firstNodeId   开始节点 ID
     * @param lastNodeId    结束节点 ID
     * @param curTaskId     taskID
     * @param curTaskDefKey 模型节点
     */
    public void addDynamicUserTaskByStr(String procDefId, String procInstId, ProcessEngine processEngine, List<String> assigns,
                                        String firstNodeId, String lastNodeId, String curTaskId, String curTaskDefKey) {
        // 自动生成taskModel
        List<TaskModel> taskModelList = assigns.stream().map(assign -> ActivityUtils.buildTaskModel(
                "dynamicUserTask_" + RandomUtil.randomString(8)
                , "动态审批节点"
                , assign
        )).collect(Collectors.toList());

        addDynamicUserTask(procDefId, procInstId, processEngine, taskModelList, firstNodeId, lastNodeId, true, true, curTaskId, curTaskDefKey);
    }

    /**
     * @param procDefId     流程定义 ID
     * @param procInstId    流程实例 ID
     * @param processEngine 流程引擎
     * @param taskModelList 节点列表
     * @param firstNodeId   开始节点 ID
     * @param lastNodeId    结束节点 ID
     * @param persistence   是否持久化
     * @param onset         是否需要立即跳转
     * @param taskId        taskID
     * @param activityId    当前任务定义id
     */
    public void addDynamicUserTask(String procDefId, String procInstId, ProcessEngine processEngine, List<TaskModel> taskModelList,
                                   String firstNodeId, String lastNodeId, boolean persistence, boolean onset, String taskId, String activityId) {
        ManagementService managementService = processEngine.getManagementService();
        HistoryService historyService = processEngine.getHistoryService();
        // 通过缓存获取
        ProcessDefinitionCacheEntry processDefinitionCacheEntry = managementService.executeCommand(new GetProcessDefinitionCacheEntryCmd(procDefId));
        Process process = processDefinitionCacheEntry.getProcess();
        // 批量生成任务, 循环遍历 TaskModel
        List<UserTask> userTaskList = Lists.newArrayList();
        taskModelList.forEach(taskModel -> {
            UserTask userTask = ActivityUtils.convertToUserTask(taskModel, processEngine);
            userTaskList.add(userTask);
            process.addFlowElement(userTask);
        });

        // 修改上一个用户任务的指向
        UserTask startTask = userTaskList.get(0);
        FlowElement currentActElement = process.getFlowElement(activityId);
        FlowNode curActNode = (FlowNode) currentActElement;
        List<SequenceFlow> incomingFlows = curActNode.getIncomingFlows();
        for (SequenceFlow incoming : incomingFlows) {
            incoming.setTargetRef(startTask.getId());
            incoming.setTargetFlowElement(startTask);
        }

        // 构造并添加连线
        for (int i = 0; i < userTaskList.size(); ++i) {
            UserTask curTask = userTaskList.get(i);
            SequenceFlow sequenceFlow = null;
            if (i == userTaskList.size() - 1) {
                // 最后一个节点指向原路径的下一个节点
                sequenceFlow = ActivityUtils.buildSequenceFlow(
                        curTask.getId() + "-->" + lastNodeId,
                        curTask.getId() + "-->" + lastNodeId,
                        curTask.getId(),
                        lastNodeId);
                sequenceFlow.setTargetRef(lastNodeId);
                sequenceFlow.setTargetFlowElement(process.getFlowElement(lastNodeId));
            } else {
                // 如果不是最后一个
                UserTask nextUserTask = userTaskList.get(i + 1);
                sequenceFlow = ActivityUtils.buildSequenceFlow(
                        curTask.getId() + "-->" + nextUserTask.getId(),
                        curTask.getId() + "-->" + nextUserTask.getId(),
                        curTask.getId(),
                        nextUserTask.getId());
                sequenceFlow.setTargetRef(nextUserTask.getId());
                sequenceFlow.setTargetFlowElement(nextUserTask);
            }
            curTask.setOutgoingFlows(Arrays.asList(sequenceFlow));
            process.addFlowElement(sequenceFlow);
        }
        log.info("process: {}", process);
        // 更新缓存
        processDefinitionCacheEntry.setProcess(process);
        // 如果需要生效
        if (onset) {
            // 默认删除当前节点
            historyService.deleteHistoricTaskInstance(taskId);
            managementService.executeCommand(new TaskJumpCmd(taskId, startTask.getId(), null));
        }
        // 如果需要持久化
        if (persistence) {
            // 记录当前节点id
            persistenceToDB(procDefId, procInstId, firstNodeId, activityId, lastNodeId, taskModelList, null, processEngine);
        }
    }

    /**
     * @param procDefId     流程定义
     * @param procInstId    流程实例
     * @param processEngine 流程引擎
     * @param assigns       分配人列表
     * @param firstNodeId   开始节点
     * @param lastNodeId    结束节点
     * @param persistence   是否持久化
     * @param onset         是否需要立即跳转
     * @param taskId        当前任务id
     * @param activityId    当前任务actId
     * @author zhonglj
     * @date 2024-11-18 17:03
     **/
    public void addSignUserTask(String procDefId, String procInstId, ProcessEngine processEngine, List<String> assigns,
                                String firstNodeId, String lastNodeId, boolean persistence, boolean onset, String taskId, String activityId) {
        ManagementService managementService = processEngine.getManagementService();
        HistoryService historyService = processEngine.getHistoryService();
        TaskService taskService = processEngine.getTaskService();
        // 通过缓存获取
        ProcessDefinitionCacheEntry processDefinitionCacheEntry = managementService.executeCommand(new GetProcessDefinitionCacheEntryCmd(procDefId));
        if (processDefinitionCacheEntry == null) {
            return;
        }
        Process process = processDefinitionCacheEntry.getProcess();

        // 创建用户任务
        UserTask signTask = new UserTask();
        if (StrUtil.isBlank(activityId)) {
            activityId = "signUserTask_" + RandomUtil.randomString(8);
        }
        signTask.setId(activityId);
        signTask.setName("动态加签节点");
        ParallelMultiInstanceBehavior behavior = ActivityUtils.createParallelMultiInstanceBehavior(signTask, processEngine);
        ProcessEngineConfigurationImpl config = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ExpressionManager expressionManager = config.getExpressionManager();
        // 多实例集合
        Expression collection = expressionManager.createExpression("${assigneeList}");
        // 通过条件为全部同意
        Expression completion = expressionManager.createExpression("${finish || nrOfCompletedInstances == nrOfInstances}");
        behavior.setCollectionExpression(collection);
        behavior.setCollectionElementVariable("assignee");
        behavior.setCompletionConditionExpression(completion);
        signTask.setBehavior(behavior);
        signTask.setAssignee("${assignee}");

        // 添加用户任务到流程中
        process.addFlowElement(signTask);
        SequenceFlow backToLastNodeSequenceFlow = ActivityUtils.buildSequenceFlow(
                signTask.getId() + "-->" + lastNodeId,
                signTask.getName() + "-->" + lastNodeId,
                signTask.getId(),
                lastNodeId
        );
        backToLastNodeSequenceFlow.setTargetFlowElement(process.getFlowElement(lastNodeId));
        process.addFlowElement(backToLastNodeSequenceFlow);
        signTask.setOutgoingFlows(Arrays.asList(backToLastNodeSequenceFlow));

        // 更新缓存
        processDefinitionCacheEntry.setProcess(process);
        // 立即生效
        if (onset) {
            boolean deleteTask = false;
            String signComment = "加签前审,同意";
            if (firstNodeId.equals(lastNodeId)) {
                deleteTask = true;
                signComment = "加签后审";
            }
            HashMap<String, Object> map = MapUtil.of("assigneeList", assigns);
            map.put("finish", false);

            taskService.addComment(taskId, procInstId, signComment);
            managementService.executeCommand(new TaskJumpCmd(taskId, signTask.getId(), map));
            if (deleteTask) {
                historyService.deleteHistoricTaskInstance(taskId);
            }
        }
        // 持久化
        if (persistence) {
            persistenceToDB(procDefId, procInstId, firstNodeId, signTask.getId(), lastNodeId, null, assigns, processEngine);
        }
    }

    /**
     * 将加签的任务节点添加到数据库
     *
     * @param procDefId
     * @param procInstId
     * @param firstNodeId
     * @param signActId     动态新增的加签节点id
     * @param lastNodeId
     * @param taskModelList
     * @param processEngine
     */
    private void persistenceToDB(String procDefId, String procInstId, String firstNodeId, String signActId, String lastNodeId, List<TaskModel> taskModelList, List<String> assigns, ProcessEngine processEngine) {
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        SqlSession sqlSession = processEngineConfiguration.getSqlSessionFactory().openSession();
        ActAddSignMapper addSignMapper = sqlSession.getMapper(ActAddSignMapper.class);

        TmpActivityModel tmpActivityModel = new TmpActivityModel();
        tmpActivityModel.setFirstId(firstNodeId);
        tmpActivityModel.setLastId(lastNodeId);
        if (CollUtil.isNotEmpty(taskModelList)) {
            tmpActivityModel.setActivityList(taskModelList);
        } else if (CollUtil.isNotEmpty(assigns)) {
            tmpActivityModel.setAssignList(assigns);
        }

        ActAddSignEntity addSign = new ActAddSignEntity();
        addSign.setProcessDefinitionId(procDefId);
        addSign.setProcessInstanceId(procInstId);
        addSign.setPropertiesText(JSON.toJSONString(tmpActivityModel));
        addSign.setActivityId(signActId);
        addSign.setCreateBy(LoginUserUtil.getUserInfo().getLoginUserVO().getId().toString());
        addSignMapper.activityInsert(addSign);
    }
}
